/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.je.bpm.engine.impl.bpmn.behavior;

import com.google.common.base.Strings;
import com.je.bpm.core.model.Activity;
import com.je.bpm.core.model.BpmnModel;
import com.je.bpm.core.model.FlowElement;
import com.je.bpm.core.model.process.SubProcess;
import com.je.bpm.core.model.task.KaiteBaseUserTask;
import com.je.bpm.engine.ActivitiException;
import com.je.bpm.engine.ActivitiIllegalArgumentException;
import com.je.bpm.engine.delegate.BpmnError;
import com.je.bpm.engine.delegate.DelegateExecution;
import com.je.bpm.engine.delegate.DelegateHelper;
import com.je.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import com.je.bpm.engine.impl.cmd.SubmitTypeEnum;
import com.je.bpm.engine.impl.context.Context;
import com.je.bpm.engine.impl.persistence.entity.ExecutionEntity;
import com.je.bpm.engine.impl.persistence.entity.ExecutionEntityImpl;
import com.je.bpm.engine.impl.persistence.entity.ExecutionEntityManager;
import com.je.bpm.engine.upcoming.UpcomingCommentInfoDTO;

import java.util.*;

/**
 * 顺序多实例行为
 */
public class SequentialMultiInstanceBehavior extends MultiInstanceActivityBehavior {

    private static final long serialVersionUID = 1L;

    public SequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) {
        super(activity, innerActivityBehavior);
    }

    /**
     * Handles the sequential case of spawning the instances. Will only create one instance, since at most one instance can be active.
     */
    @Override
    protected int createInstances(DelegateExecution multiInstanceExecution) {
        String assigneeUser = getAssigneeUser(multiInstanceExecution);
        //处理人员
        setLoopVariable(multiInstanceExecution, MultiInstanceActivityBehavior.PROCESSING_USERS_INFO, Arrays.asList(assigneeUser.split(",")));
        int nrOfInstances = resolveNrOfInstances(multiInstanceExecution);
        if (nrOfInstances == 0) {
            return nrOfInstances;
        } else if (nrOfInstances < 0) {
            throw new ActivitiIllegalArgumentException("Invalid number of instances: must be a non-negative integer value" + ", but was " + nrOfInstances);
        }
        //添加审批告知
        addApprovalNotice(Context.getCommandContext(), multiInstanceExecution, null, null, assigneeUser);
        // Create child execution that will execute the inner behavior
        ExecutionEntity childExecution = Context.getCommandContext().getExecutionEntityManager()
                .createChildExecution((ExecutionEntity) multiInstanceExecution);
        childExecution.setCurrentFlowElement(multiInstanceExecution.getCurrentFlowElement());
        multiInstanceExecution.setMultiInstanceRoot(true);
        multiInstanceExecution.setActive(false);

        // Set Multi-instance variables
        addCounterVariable((ExecutionEntityImpl) multiInstanceExecution);
        setLoopVariable(multiInstanceExecution, NUMBER_OF_INSTANCES, nrOfInstances);
        setLoopVariable(multiInstanceExecution, NUMBER_OF_COMPLETED_INSTANCES, 0);
        setLoopVariable(multiInstanceExecution, NUMBER_OF_ACTIVE_INSTANCES, 1);
        setLoopVariable(childExecution, getCollectionElementIndexVariable(), 0);
        logLoopDetails(multiInstanceExecution, "initialized", 0, 0, 1, nrOfInstances);

        executeOriginalBehavior(childExecution, 0);
        return nrOfInstances;
    }

    /**
     * Called when the wrapped {@link com.je.bpm.engine.impl.delegate.ActivityBehavior} calls the {@link AbstractBpmnActivityBehavior#leave(DelegateExecution)} method. Handles the completion of one instance, and executes the logic for
     * the sequential behavior.
     */
    @Override
    public void leave(DelegateExecution childExecution) {
        DelegateExecution multiInstanceRootExecution = getMultiInstanceRootExecution(childExecution);
        int nrOfInstances = getLoopVariable(multiInstanceRootExecution, NUMBER_OF_INSTANCES);
        int loopCounter = getLoopVariable(multiInstanceRootExecution, getCollectionElementIndexVariable()) + 1;
        int nrOfCompletedInstances = getLoopVariable(multiInstanceRootExecution, NUMBER_OF_COMPLETED_INSTANCES) + 1;
        int nrOfActiveInstances = getLoopVariable(multiInstanceRootExecution, NUMBER_OF_ACTIVE_INSTANCES);
        String type = getStringVariable(multiInstanceRootExecution, COUNTERSIGN_PASS_TYPE);
        Boolean isCounter = (!Strings.isNullOrEmpty(type)) ? true : false;
        if (isCounter) {
            buildCounterInfo(childExecution, multiInstanceRootExecution);
        }

        setLoopVariable(multiInstanceRootExecution, NUMBER_OF_COMPLETED_INSTANCES, nrOfCompletedInstances);
        setLoopVariable(multiInstanceRootExecution, getCollectionElementIndexVariable(), loopCounter);
        if (isCounter) {
            setCounterInfo(multiInstanceRootExecution);
        }

        logLoopDetails(childExecution, "instance completed", loopCounter, nrOfCompletedInstances, nrOfActiveInstances, nrOfInstances);

        updateResultCollection(childExecution, multiInstanceRootExecution);

        Context.getCommandContext().getHistoryManager().recordActivityEnd((ExecutionEntity) childExecution, null);
        callActivityEndListeners(childExecution);
        //通过逻辑
        if (loopCounter >= nrOfInstances || completionConditionSatisfied(multiInstanceRootExecution)) {
            countersigned(childExecution, multiInstanceRootExecution);
        } else {
            try {
                if (childExecution.getCurrentFlowElement() instanceof SubProcess) {
                    ExecutionEntityManager executionEntityManager = Context.getCommandContext().getExecutionEntityManager();
                    ExecutionEntity executionToContinue = executionEntityManager.createChildExecution((ExecutionEntity) multiInstanceRootExecution);
                    executionToContinue.setCurrentFlowElement(childExecution.getCurrentFlowElement());
                    executionToContinue.setScope(true);
                    setLoopVariable(executionToContinue, getCollectionElementIndexVariable(), loopCounter);
                    executeOriginalBehavior(executionToContinue, loopCounter);
                } else {
                    executeOriginalBehavior(childExecution, loopCounter);
                }
                dispatchActivityCompletedEvent(childExecution);
            } catch (BpmnError error) {
                // re-throw business fault so that it can be caught by an Error
                // Intermediate Event or Error Event Sub-Process in the process
                throw error;
            } catch (Exception e) {
                throw new ActivitiException("Could not execute inner activity behavior of multi instance behavior", e);
            }
        }
    }


    public void countersigned(DelegateExecution childExecution, DelegateExecution multiInstanceRootExecution) {
        propagateLoopDataOutputRefToProcessInstance((ExecutionEntity) multiInstanceRootExecution);
        removeLocalLoopVariable(childExecution, getCollectionElementIndexVariable());
        multiInstanceRootExecution.setMultiInstanceRoot(false);
        multiInstanceRootExecution.setScope(false);
        multiInstanceRootExecution.setCurrentFlowElement(childExecution.getCurrentFlowElement());
        Context.getCommandContext().getExecutionEntityManager().deleteChildExecutions((ExecutionEntity) multiInstanceRootExecution, "MI_END");
        dispatchActivityCompletedEvent(childExecution);
        ProcessEngineConfigurationImpl processEngineConfiguration = Context.getProcessEngineConfiguration();
        BpmnModel bpmnModel = processEngineConfiguration.getRepositoryService().getBpmnModel(multiInstanceRootExecution.getProcessDefinitionId());
        //否决退回
        if (!isPass(multiInstanceRootExecution)) {
            List<Map<String, String>> nodeInfo = getNodeInfo(multiInstanceRootExecution, bpmnModel);
            Map<String, String> map = nodeInfo.get(0);
            String directTask = map.get("directTaskId");
            String prevAssignee = map.get("prevAssignee");
            FlowElement targetFlowElement = bpmnModel.getFlowElement(directTask);
            if (!(targetFlowElement instanceof KaiteBaseUserTask)) {
                throw new ActivitiException("Only kaite user task can goback.");
            }
            getCommandContext().addAttribute("isVeto", true);
            KaiteBaseUserTask targetBaseUserTask = (KaiteBaseUserTask) targetFlowElement;
            Map<String, Object> transientVariables = new HashMap<>();
            transientVariables.put(directTask, prevAssignee);
            multiInstanceRootExecution.setTransientVariables(transientVariables);
            multiInstanceRootExecution.setCurrentFlowElement(targetFlowElement);
            dispatchActivityCompletedEvent(childExecution);
            Map<String, Object> bean = null;
            Object variable = Context.getCommandContext().getAttribute(KaiteBaseUserTaskActivityBehavior.UPCOMINGINFO);
            if (variable != null && variable instanceof UpcomingCommentInfoDTO) {
                UpcomingCommentInfoDTO upcomingCommentInfoDTO = (UpcomingCommentInfoDTO) variable;
                bean = upcomingCommentInfoDTO.getBean();
            }
            Context.getCommandContext().addAttribute(KaiteBaseUserTaskActivityBehavior.UPCOMINGINFO, DelegateHelper.buildUpcomingInfo(bean, "", SubmitTypeEnum.VETO,
                    multiInstanceRootExecution.getProcessInstanceBusinessKey(), "", null));
            //会签多人否决
            //添加审批告知
            String submitType = SubmitTypeEnum.VETO.getName();
            setVar(multiInstanceRootExecution, SubmitTypeEnum.VETO, submitType);
            Set<String> listIds = new HashSet<>();
            listIds.add(childExecution.getId());
            listIds.add(childExecution.getParent().getId());
            //如果目标任务属于多实例节点，则要根据多实例要求创建多个任务（串行创建一个，并行创建多个）
            setDirectTaskIdVar(multiInstanceRootExecution);
            leaveRemoveCounterVar(multiInstanceRootExecution);
            if (targetBaseUserTask.hasMultiInstanceLoopCharacteristics()) {
                Context.getAgenda().planContinueMultiInstanceOperation((ExecutionEntity) multiInstanceRootExecution);
            } else {
                Context.getAgenda().planContinueProcessOperation((ExecutionEntity) multiInstanceRootExecution);
            }
        } else {
            Map<String, Object> bean = null;
            Object variable = Context.getCommandContext().getAttribute(KaiteBaseUserTaskActivityBehavior.UPCOMINGINFO);
            if (variable != null && variable instanceof UpcomingCommentInfoDTO) {
                UpcomingCommentInfoDTO upcomingCommentInfoDTO = (UpcomingCommentInfoDTO) variable;
                bean = upcomingCommentInfoDTO.getBean();
            }
            Context.getCommandContext().addAttribute(KaiteBaseUserTaskActivityBehavior.UPCOMINGINFO, DelegateHelper.buildUpcomingInfo
                    (bean, "", SubmitTypeEnum.PASS, multiInstanceRootExecution.getProcessInstanceBusinessKey(), "", null));
            //会签多人通过
            //添加审批告知
            String submitType = SubmitTypeEnum.PASS.getName();
            setVar(multiInstanceRootExecution, SubmitTypeEnum.PASS, submitType);
            setDirectTaskIdVar(multiInstanceRootExecution);
            leaveRemoveCounterVar(multiInstanceRootExecution);
            super.leave(multiInstanceRootExecution);
        }
    }


}
